﻿using System;
using System.Collections.Concurrent;
using System.Reflection;

using zijian666.Core.Abstractions;

namespace zijian666.Core;

/// <summary>
/// 缓存的反射编译程序
/// </summary>
internal class CachedReflectCompiler : IReflectCompiler
{
    /// <summary>
    /// 反射编译程序
    /// </summary>
    private readonly IReflectCompiler _reflectCompiler;
    /// <summary>
    /// 构造函数
    /// </summary>
    public CachedReflectCompiler(IReflectCompiler reflectCompiler) => _reflectCompiler = reflectCompiler ?? throw new ArgumentNullException(nameof(reflectCompiler));
    /// <summary>
    /// 缓存
    /// </summary>
    private readonly ConcurrentDictionary<MemberInfo, Delegate> _cache = new();
    
    /// <inheritdoc />
    public ObjectCreator<Object> CompileCreator<Object>(ConstructorInfo constructor)
        => (ObjectCreator<Object>)_cache.GetOrAdd(constructor, x => _reflectCompiler.CompileCreator<object>((ConstructorInfo)x));

    /// <inheritdoc />
    public MemberGetter<Value> CompileGetter<Value>(PropertyInfo property)
        => (MemberGetter<Value>)_cache.GetOrAdd(property, x => _reflectCompiler.CompileGetter<Value>((PropertyInfo)x));

    /// <inheritdoc />
    public MemberSetter<Value> CompileSetter<Value>(PropertyInfo property)
        => (MemberSetter<Value>)_cache.GetOrAdd(property, x => _reflectCompiler.CompileSetter<Value>((PropertyInfo)x));

    /// <inheritdoc />
    public MemberGetter<Value> CompileGetter<Value>(FieldInfo field)
        => (MemberGetter<Value>)_cache.GetOrAdd(field, x => _reflectCompiler.CompileGetter<Value>((FieldInfo)x));

    /// <inheritdoc />
    public MemberSetter<Value> CompileSetter<Value>(FieldInfo field)
        => (MemberSetter<Value>)_cache.GetOrAdd(field, x => _reflectCompiler.CompileSetter<Value>((FieldInfo)x));

    /// <inheritdoc />
    public MethodCaller<Result> CompileCaller<Result>(MethodInfo method)
        => (MethodCaller<Result>)_cache.GetOrAdd(method, x => _reflectCompiler.CompileCaller<Result>((MethodInfo)x));
    public static CachedReflectCompiler Cached(IReflectCompiler reflectCompiler) => reflectCompiler is CachedReflectCompiler cached ? cached : new CachedReflectCompiler(reflectCompiler);
}
